#pragma once
#include <sstream>
#include <string>
#include <algorithm>
#include <opencv2\core\core.hpp>
#include <QString>

template<class In, class Out>
class Converter
{
public:
	static Out Convert(In value)
	{
		std::stringstream ss;
		ss << value;
		Out result;
		ss >> result;
		return result;
	}	
};

template<>
class Converter<std::string, char>
{
public:
	static char Convert(std::string value)
	{
		std::stringstream ss;
		ss << value;
		int result;
		ss >> result;
		return (char)result;
	}
};

template<>
class Converter<std::string, unsigned char>
{
public:
	static unsigned char Convert(std::string value)
	{
		std::stringstream ss;
		ss << value;
		int result;
		ss >> result;
		return (unsigned char)result;
	}
};

template<>
class Converter<cv::Mat, std::string>
{
public:
	static std::string Convert(cv::Mat value)
	{
		std::stringstream ss;
		int typeChannels = (value.channels() - 1)*8;
		int type = value.type() - typeChannels;

		ss << "(" << type << "," << value.channels() << "," << value.rows << "," << value.cols << ")";
		ss << value;
	
		std::string result = ss.str();
		//usuwanie spacji
		ss.str(std::string());
		for (std::string::iterator it = result.begin(); it != result.end(); ++it)
		{
			if (*it != ' ')
				ss << *it;
		}

		return ss.str();
	}
};

template<>
class Converter<std::string, cv::Mat>
{
public:
	static cv::Mat Convert(std::string value)
	{
		std::vector<std::string> tokens;
		std::stringstream ss;
		bool building = false;
		for (std::string::iterator it = value.begin(); it != value.end(); ++it)
		{
			if (building)
			{
				if (*it == ',' || *it == ';' || *it == '(' || *it == ')' || *it == '[' || *it == ']')
				{
					building = false;
					tokens.push_back(ss.str());
					ss.str(std::string());
					continue;
				}
				else
				{
					ss << *it;
				}
			}
			else
			{
				if (*it == ',' || *it == ';' || *it == '(' || *it == ')' || *it == '[' || *it == ']')
				{
					continue;
				}
				else
				{
					building = true;
					ss << *it;
					continue;
				}
			}
		}

		int type = Converter<std::string, int>::Convert(tokens[0]);
		
		switch(type)
		{
		case CV_8U:
			return MatrixFromTokens<unsigned char>(tokens);
		case CV_8S:
			return MatrixFromTokens<char>(tokens);
		case CV_16U:
			return MatrixFromTokens<unsigned short>(tokens);
		case CV_16S:
			return MatrixFromTokens<short>(tokens);
		case CV_32S:
			return MatrixFromTokens<int>(tokens);
		case CV_32F:
			return MatrixFromTokens<float>(tokens);
		case CV_64F:
			return MatrixFromTokens<double>(tokens);
		}

		return cv::Mat();
	}

private:
	//convert cv::Mat
	template<class T>
	static cv::Mat MatrixFromTokens(const std::vector<std::string> tokens)
	{
		int type = Converter<std::string, int>::Convert(tokens[0]);
		int channels = Converter<std::string, int>::Convert(tokens[1]);
		int rows = Converter<std::string, int>::Convert(tokens[2]);
		int cols = Converter<std::string, int>::Convert(tokens[3]);

		int t = CV_MAKETYPE(type, channels);
		cv::Mat matrix = cv::Mat(rows, cols, t);
		int tokenIndex = 4;
		for (int i = 0; i < rows; ++i)
		{
			T *p = matrix.ptr<T>(i);
			for (int j = 0; j < cols; ++j)
			{
				for (int c = 0; c < channels; ++c)
				{
					T val = Converter<std::string, T>::Convert(tokens[tokenIndex++]);
					p[j * channels + c] = val;
				}
			}
		}

		return matrix.clone();
	}

};



template<class Out>
class Converter<QString, Out>
{
public:
	static Out Convert(QString value)
	{
		std::stringstream ss;
		ss << value.toStdString();
		Out result;
		ss >> result;
		return result;
	}
};

template<class In>
class Converter<In, QString>
{
public:
	static QString Convert(In value)
	{
		std::stringstream ss;
		ss << value;
		std::string result;
		while (!ss.eof())
		{
			std::string part;
			ss >> part;
			result.append(part);
		}
		return QString::fromStdString(result);
	}
};

template<>
class Converter<QString, std::string>
{
public:
	static std::string Convert(QString value)
	{
		return value.toStdString();
	}
};

template<>
class Converter<std::string, QString>
{
public:
	static QString Convert(std::string value)
	{
		return QString::fromStdString(value);
	}
};
